package data.partition;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ProcessData {

	public static String folder = "/home/quyet/test/dataMulticore";

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		// folder = args[0];
		// String isRunPartition = args[1];
		// String part = args[2];
		// int p = Integer.parseInt(part);

		String isRunPartition = "2";
		int p = 64;

		folder = args[0];
		
		long startTime = System.currentTimeMillis();

		if (isRunPartition.equals("1"))		
			GetData(folder + "/data.txt");
		else if (isRunPartition.equals("2"))
			GraphPartition(p, folder + "/edges.txt.result_1_of_1");

		long stopTime = System.currentTimeMillis();
		long elapsedTime = stopTime - startTime;

		System.out.println("\nFinished after: " + elapsedTime + " ms");
				

		// ArrayList<Vertex> listViNode = new ArrayList<Vertex>();
		// ArrayList<Vertex> listInNode = new ArrayList<Vertex>();
		// ArrayList<Vertex> listOutNode = new ArrayList<Vertex>();
		//
		// LoadData(folder + "/partition9.txt", listViNode, listInNode,
		// listOutNode);
		//
		// System.out.print("FRAGMENT\n");
		// for (Vertex v : listViNode) {
		// System.out.print(v.name + "," + v.label + ": ");
		// for (Edge e : v.neighbors) {
		// System.out.print(e.target + "\t");
		// }
		// System.out.print("\n");
		// }
		//
		// System.out.print("\nIN-NODES\n");
		// for (Vertex v : listInNode) {
		// System.out.print(v.name + "," + v.label + ": ");
		// for (Edge e : v.neighbors) {
		// System.out.print(e.target + "\t");
		// }
		// System.out.print("\n");
		// }
		//
		// System.out.print("\nOUT-NODES\n");
		// for (Vertex v : listOutNode) {
		// System.out.print(v.name + "," + v.label + ": ");
		// for (Edge e : v.neighbors) {
		// System.out.print(e.target + "\t");
		// }
		// System.out.print("\n");
		// }
	}

	/**
	 * @param filePath
	 */
	public static void LoadData(String filePath, ArrayList<Vertex> listViNode,
			ArrayList<Vertex> listInNode, ArrayList<Vertex> listOutNode) {
		try {

			FileReader f = new FileReader(filePath);
			BufferedReader bNode = new BufferedReader(f);

			String strLine = null;
			while ((strLine = bNode.readLine()) != null) {
				String[] row = strLine.split("\t");
				String[] node = row[0].split(":"); // split to get node id &
													// determine input node

				// node in fragment
				Vertex vi;
				if (row.length >= 2)
					vi = new Vertex(Integer.parseInt(node[0]), row[1]);
				else
					vi = new Vertex(Integer.parseInt(node[0]));

				if (!node[1].equals("2")) {
					// get neighbors
					for (int i = 2; i < row.length; i++) {
						Vertex vj = new Vertex(Integer.parseInt(row[i]));
						if (!listViNode.contains(vj)) {
							listViNode.add(vj);
						}
						vi.neighbors.add(new Edge(Integer.parseInt(row[i])));
					}
				}

				// add vertex to fragment if not in V
				if (!listViNode.contains(vi)) {
					listViNode.add(vi);
					if (node[1].equals("1"))
						listInNode.add(vi);
					// if vi is out node then unsure has added in listVi

				} else {
					// set label if exists in V
					int index = listViNode.indexOf(vi);

					if (row.length >= 2) {
						listViNode.get(index).label = row[1]; // set label
						listViNode.get(index).neighbors = vi.neighbors;
					}

					if (node[1].equals("1"))
						listInNode.add(listViNode.get(index));

					if (node[1].equals("2"))
						listOutNode.add(listViNode.get(index));
				}
			}
			bNode.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void GatherPartition(int key,
			HashMap<Integer, ArrayList<Integer>> listP,
			ArrayList<Node> listNodes, ArrayList<Integer> listIds) throws IOException {

		FileWriter fwPart = new FileWriter(folder + "/partition" + key + ".txt");
		BufferedWriter bwPart = new BufferedWriter(fwPart);
		String node = "";

		ArrayList<String> listOutNodes = new ArrayList<String>();
		ArrayList<Integer> listV = listP.get(key);

		for (Integer v : listV) {
			node = "";
			int idx = listIds.indexOf(v);

			if (idx >= 0) {

				// check v is in-node?
				boolean check = false;
				for (Integer t : listP.keySet()) {
					if (t != key) {
						ArrayList<Integer> listN = listP.get(t);
						for (int n : listN) {
							int index = listIds.indexOf(n);
							if (index >= 0) {
								Node nt = listNodes.get(index);
								if (nt.neighbors.contains(v)) {
									check = true;
									break;
								}
							}
						}
					}
				}
				// end check v in in-node

				// set in - out node
				Node vi = listNodes.get(idx);
				node = vi.id + ":";
				if (check)
					node += "1\t";
				else
					node += "0\t";

				node += vi.label + "\t";

				for (int s : vi.neighbors) {
					if (!listV.contains(s)) {
						int index = listIds.indexOf(s);
						if (index >= 0) {
							Node a = listNodes.get(index);
							if (!listOutNodes.contains(s + ":2\t" + a.label))
								listOutNodes.add(s + ":2\t" + a.label);
						} else {
							listOutNodes.add(s + ":2");
						}
					}
					node += s + "\t";
				}

				node = node.substring(0, node.length() - 1);
			} else {
				node = v + ":0";
			}

			bwPart.write(node + "\n");
		}

		for (String outnode : listOutNodes)
			bwPart.write(outnode + "\n");

		bwPart.close();

	}

	public static void GraphPartition(int partitions, String filePartition) {
		try {			
			
			final HashMap<Integer, ArrayList<Integer>> listP = new HashMap<Integer, ArrayList<Integer>>();
			ArrayList<ArrayList<Integer>> tmp = new ArrayList<ArrayList<Integer>>(
					partitions);

			String strLine = null;

			FileReader frPart = new FileReader(new File(filePartition));
			BufferedReader brPart = new BufferedReader(frPart);

			for (int i = 0; i < partitions; i++) {
				tmp.add(new ArrayList<Integer>());
			}

			int p = 0; // partition
			while ((strLine = brPart.readLine()) != null) {
				String[] row = strLine.split("\t");
				p = Integer.parseInt(row[1]) - 1;
				tmp.get(p).add(Integer.parseInt(row[0]));
			}
			for (int i = 0; i < partitions; i++) {
				listP.put(i + 1, tmp.get(i));
			}

			brPart.close();

			// Load primitive data

			FileReader fNode = new FileReader(new File(folder + "/nodes.txt"));
			BufferedReader bNode = new BufferedReader(fNode);
			strLine = null;

			final ArrayList<Node> listNodes = new ArrayList<Node>();
			final ArrayList<Integer> listIds = new ArrayList<Integer>();

			while ((strLine = bNode.readLine()) != null) {
				String[] row = strLine.split("\t");

				ArrayList<Integer> neighbors = new ArrayList<Integer>();
				for (int i = 3; i < row.length; i++)
					neighbors.add(Integer.parseInt(row[i]));

				listNodes.add(new Node(Integer.parseInt(row[0]), row[2], neighbors));
				listIds.add(Integer.parseInt(row[0]));
			}
			bNode.close();

			int nrOfProcessors = Runtime.getRuntime().availableProcessors();			
			if(nrOfProcessors>=2) nrOfProcessors -=1;
			System.out.println("Processors: "+nrOfProcessors);
			
			ExecutorService eservice = Executors
					.newFixedThreadPool(nrOfProcessors);

			for (Integer k : listP.keySet()) {
				final int key = k;
				eservice.submit(new Runnable() {
	                    @Override
	                    public void run() {
	                    	try {
								GatherPartition(key, listP, listNodes, listIds);
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
	                    }
	                });				
			}	
			
			eservice.shutdown();	
			

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void GetData(String filepath) {
		// TODO Auto-generated method stub

		try {
			FileReader fr = new FileReader(new File(filepath));
			BufferedReader br = new BufferedReader(fr);

			FileWriter fNode = new FileWriter(folder + "/nodes.txt");
			FileWriter fEdge = new FileWriter(folder + "/edges.txt");
			FileWriter fLabel = new FileWriter(folder + "/labels.txt");
			BufferedWriter bwNode = new BufferedWriter(fNode);
			BufferedWriter bwEdge = new BufferedWriter(fEdge);
			BufferedWriter bwLalel = new BufferedWriter(fLabel);

			ArrayList<String> listL = new ArrayList<String>();

			String strLine;
			ArrayList<Node> V = new ArrayList<Node>();
			int id = 1;

			while ((strLine = br.readLine()) != null) {

				String[] row = strLine.split("\t");
				// if (row.length >= 2) {
				if (row.length >= 4) {

					if (!listL.contains(row[3])) {
						listL.add(row[3]);
						bwLalel.write(row[3] + "\n");
					}

					Node ni = new Node(id, row[0], row[3]); // row[3] => 1
					Node ntmp = new Node(row[0]);

					if (!V.contains(ntmp)) {
						V.add(ni);
						id += 1;
					} else {
						ni.id = (V.indexOf(ntmp) + 1);
					}

					String node = ni.id + "\t" + row[0] + "\t" + row[3] + "\t"; // row[3]
																				// =>
																				// 1

					for (int i = 9; i < row.length; i++) {
						// for (int i = 2; i < row.length; i++) {
						Node nj = new Node(id, row[i]);
						if (!V.contains(nj)) {
							V.add(nj);
							bwEdge.write(ni.id + " " + id + "\n");
							node += id + "\t";
							id++;
						} else {
							bwEdge.write(ni.id + " " + (V.indexOf(nj) + 1)
									+ "\n");
							node += (V.indexOf(nj) + 1) + "\t";
						}
					}

					node = node.substring(0, node.length() - 1);
					bwNode.write(node + "\n");
				}
			}
			System.out.print("Vertices: " + V.size());
			br.close();
			bwLalel.close();
			bwNode.close();
			bwEdge.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

// Vertex define
class Node {
	int id;
	String name;
	String label;
	ArrayList<Integer> neighbors;

	public Node() {
		this.name = "";
		this.label = "";
	}

	public Node(String name) {
		this.name = name;
	}

	public Node(Integer id, String name) {
		this.id = id;
		this.name = name;
	}

	public Node(Integer id, String label, ArrayList<Integer> neighbors) {
		this.id = id;
		this.label = label;
		this.neighbors = neighbors;
	}

	public Node(Integer id, String name, String label) {
		this.id = id;
		this.name = name;
		this.label = label;
	}

	@Override
	public boolean equals(Object obj) {
		boolean check = false;
		if (obj == null)
			return false;
		else if (obj == this)
			return true;
		else {
			if (obj instanceof Node && obj.getClass() == this.getClass()) {
				final Node t = (Node) obj;
				check = this.name.equals(t.name);
			}
		}
		return check;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = (37 * hash) + (null == this.name ? 0 : this.name.hashCode());
		return hash;
	}
}
